#if !defined(IMGLIB_IMAGE_H)
#define IMGLIB_IMAGE_H

#include "imglib/raster.h"
#include <vector>
#include <algorithm>
#include <xtd/type_traits.h>

namespace img {

template <typename T> class image;

///image without interpolation functions
template <typename T> 
class simple_image {
public:
	typedef T elem_type;
	typedef raster_iterator<simple_image<T>, T &> iterator;
	typedef const_raster_iterator<simple_image<T>, const T &, raster_iterator<simple_image<T>, T &> > const_iterator;
	typedef img::size_type size_type;
private:
	typedef unsigned char byte;
	byte *			_data;
	bool			_owner;
	size_type		_width;
	size_type		_height;
	size_type		_alignment;
	order_type		_order;
	size_type		_scansize;
	size_type		_bytes;
	std::vector<T*>	_scans;
	template <class U> friend class simple_image;
private:
	void update_scans() {
		_scans.resize(_height);
		unsigned int n = 0;
		if (_order == bottom_up) {
			typename std::vector<T *>::reverse_iterator it;
			for (it = _scans.rbegin(); it != _scans.rend(); ++it)
				* it = reinterpret_cast<T *>(_data + (n++) * _scansize);
		} else {
			typename std::vector<T *>::iterator it;
			for (it = _scans.begin(); it != _scans.end(); ++it)
				* it = reinterpret_cast<T *>(_data + (n++) * _scansize);
		}
	}
	void recalc(size_type w, size_type h, size_type a) {
		_width = w;
		_height = h;
		_alignment = a;
		_scansize = _width * sizeof(T);
		if (_alignment > 1)
			if ((_scansize % _alignment) != 0)
				_scansize = (_scansize / _alignment + 1) * _alignment;
		_bytes = _scansize * _height;
	}
public:
	simple_image(size_type w = 0, size_type h = 0, size_type a = 4)
		: _data(0), _owner(true), _width(0), _height(0), _alignment(1), _order(top_down), _scansize(0), _bytes(0)
		{ resize(w, h, a); }
	simple_image(size_extent e, size_type a = 4)
		: _data(0), _owner(true), _width(0), _height(0), _alignment(1), _order(top_down), _scansize(0), _bytes(0)
		{ resize(e, a); }
	simple_image(void * data, size_type w = 0, size_type h = 0, size_type a = 4)
		: _data(0), _owner(true), _width(0), _height(0), _alignment(1), _order(top_down), _scansize(0), _bytes(0)
		{ attach(data, w, h, a); }
	simple_image(void * data, size_extent e, size_type a = 4)
		: _data(0), _owner(true), _width(0), _height(0), _alignment(1), _order(top_down), _scansize(0), _bytes(0)
		{ attach(data, e, a); }
	simple_image(const simple_image & i) 
		: _data(0), _owner(true), _width(0), _height(0), _alignment(1), _order(top_down), _scansize(0), _bytes(0)
		{ operator = (i); }
	template <class R> simple_image(const R & raster) 
		: _data(0), _owner(true), _width(0), _height(0), _alignment(1), _order(top_down), _scansize(0), _bytes(0)
		{ operator = (raster); }
	~simple_image() 
		{ clear(); }
	size_type bytes() const
		{ return _bytes; }
	size_type scansize() const
		{ return _scansize; }
	size_type width() const 
		{ return _width; }
	size_type height() const
		{ return _height; }
	size_extent dim() const
		{ return size_extent(_width, _height); }
	size_type size() const
		{ return _width * _height; }
	size_type alignment() const
		{ return _alignment; }
	order_type order() const
		{ return _order; }
	bool owner() const
		{ return _owner; }
	const void * data() const
		{ return _data; }
	void * data() 
		{ return _data; }
	bool empty() const
		{ return _data == 0; }
	T * scan(size_type y)
		{ return _scans[y]; }
	const T * scan(size_type y) const
		{ return _scans[y]; }
	const T & operator () (size_type x, size_type y) const
		{ return _scans[y][x]; }
	const T & operator [] (point<size_type> const & p) const
		{ return _scans[p.y][p.x]; }
	const T & operator [] (point<int> const & p) const
		{ return _scans[p.y][p.x]; }
	T & operator () (size_type x, size_type y)
		{ return _scans[y][x]; }
	T & operator [] (point<size_type> const & p)
		{ return _scans[p.y][p.x]; }
	T & operator [] (point<int> const & p)
		{ return _scans[p.y][p.x]; }
	T operator () (float x, float y) const
		{ return interpolate(* this, x, y); }
	const_iterator begin() const
		{ return const_iterator(* this, 0, 0); }
	const_iterator end() const
		{ return const_iterator(* this, 0, _height); }
	iterator begin()
		{ return iterator(* this, 0, 0); }
	iterator end()
		{ return iterator(* this, 0, _height); }
public:
	template <class R> simple_image & operator = (const R & raster) {
		resize(raster.width(), raster.height());
		std::copy(raster.begin(), raster.end(), begin());
		return * this;
	}
	template <class U> simple_image & operator = (const image<U> & rhs) {
		*this = static_cast<const simple_image<U> &>(rhs);
		return * this;
	}
	template <class U> simple_image & operator = (const simple_image<U> & rhs) {
		resize(rhs.width(), rhs.height());
		typename std::vector<T*>::iterator tgt_line = _scans.begin();
		typename std::vector<U*>::const_iterator src_line = rhs._scans.begin();
		for (; tgt_line != _scans.end(); ++tgt_line, ++src_line)
			std::copy(*src_line, *src_line + _width, *tgt_line);
		return * this;
	}
	simple_image & operator = (const simple_image & rhs) {
		if (&rhs == this)
			return * this;
		resize(rhs.width(), rhs.height());
		if (scansize() == rhs.scansize() && order() == rhs.order())
			memcpy(data(), rhs.data(), bytes());
		else
			for (unsigned int n = 0; n < _scans.size(); ++n)
				memcpy(_scans[n], rhs._scans[n], sizeof(T) * width());
		return * this;                                                                                
	}
	void resize(size_type w, size_type h, size_type a) {
		//if (a == 0)
		//	a = _alignment;
		if (/*!_owner ||*/ w != _width || h != _height || a != _alignment) {
			size_type old_bytes = _bytes;	
			std::vector<T*> old_scans(_scans);
			byte * old_data = _data;
			bool copy_data = (_data != 0 && w == _width && h == _height);
			recalc(w, h, a);
			if (_bytes != old_bytes)
				_data = _bytes != 0 ? new byte[_bytes] : 0;
			update_scans();
			if (copy_data)
				for (unsigned int n = 0; n < _scans.size(); ++n)
					memcpy(_scans[n], old_scans[n], sizeof(T) * _width);
			if (_owner && old_data != 0 && old_data != _data)
				delete [] old_data;
			_owner = true;
		}
	}
	void resize(size_extent e, size_type a) 
		{ resize(e.sx, e.sy, a); }
	void resize(size_extent e) {
		if (dim() != e)
			resize(e.sx, e.sy, 4); 
	}
	void resize(size_type w, size_type h) 
		{ resize(size_extent(w, h)); }
	void clear() {
		if (_owner)
			resize(0, 0);
		else 
			detach();
	}
	void attach(void * data, size_type w, size_type h, size_type a = 4) {
		if (a == 0)
			a = _alignment;
		if (_owner)
			resize(0, 0);
		if (data != _data || w != _width || h != _height || a != _alignment) {
			recalc(w, h, a);
			_data = reinterpret_cast<byte *>(data);
			update_scans();
			_owner = false;
		}
	}
	void attach(void * data, size_extent e, size_type a = 4) 
		{ attach(data, e.sx, e.sy, a); }
	void attach(simple_image const & im) {
		_data = im._data;
		_owner = false;
		_width = im._width;
		_height = im._height;
		_alignment = im._alignment;
		_order = im._order;
		_scansize = im._scansize;
		_bytes = im._bytes;
		_scans = im._scans;
	}
	void take(void * data, size_type w, size_type h, size_type a = 4) {
		attach(data, w, h, a);
		_owner = true;
	}
	void take(void * data, size_extent e, size_type a = 4) 
		{ take(data, e.sx, e.sy, a); }
	void * detach() { 
		void * data = _data;
		recalc(0, 0, 0);
		_data = 0;
		_scans.clear();
		return data;
	}
	void alignment(size_type a) {
		if (_owner)
			resize(_width, _height, a);
		else
			attach(_data, _width, _height, a);
	}
	void order(order_type o) {
		if (o != _order) {
			_order = o;
			update_scans();
		}
	}
	void flip_order() {
		_order = img::flip(_order);
		update_scans();
	}
	void flip() {
		size_type src, dest; //count = _width * sizeof(T);
		for (src = 0, dest = _height - 1; src < dest; ++src, --dest) {
			//why memcpy, it's too slow? 
			//because we may avoid copy with flip_order(), 
			//but we are not going to change ordering 
			std::swap_ranges(_scans[src], _scans[src] + _width, _scans[dest]);
		}
	}	
	template <class U> void copy_attributes(const simple_image<U> & img) {
		resize(img.width(), img.height(), img.alignment());
		order(img.order());
	}
	void swap(simple_image &i) {
		std::swap(_data, i._data);
		std::swap(_owner, i._owner);
		std::swap(_width, i._width);
		std::swap(_height, i._height);
		std::swap(_alignment, i._alignment);
		std::swap(_order, i._order);
		std::swap(_scansize, i._scansize);
		std::swap(_bytes, i._bytes);
		_scans.swap(i._scans);
	}
};

template <typename T> 
class image : public simple_image<T>
{
private:
	typedef simple_image<T> base;
public:
	typedef typename xtd::type_traits<elem_type>::real_type real_elem_type;
public: //ctors & copy operators
	image(size_type w = 0, size_type h = 0, size_type a = 4) : base(w, h, a) { }
	image(size_extent e, size_type a = 4) : base(e, a) { }
	image(void * data, size_type w = 0, size_type h = 0, size_type a = 4) : base(data, w, h, a) { }
	image(void * data, size_extent e, size_type a = 4) : base(data, e, a) { }
	template <class R> image(const R & raster) : base(raster) { }
	template <class R> image & operator = (const R & raster) 
		{ return static_cast<image &>(base::operator = (raster)); }
public:
	using base::operator []; ///also support other operators
	real_elem_type operator [] (point<float> const & p) const
		{ return interpolate(* this, p.x, p.y); }
	real_elem_type operator [] (point<float> const & p) ///< this method is required to remove ambiguity when operating with not-const image
		{ return interpolate(* this, p.x, p.y); }
};

} // img

namespace std {

template <class T> 
inline void swap(img::simple_image<T> & lhs, img::simple_image<T> & rhs) {
	lhs.swap(rhs);
}

template <class T> 
inline void swap(img::image<T> & lhs, img::image<T> & rhs) {
	lhs.swap(rhs);
}

} //namespace std

#endif // IMGLIB_IMAGE_H
